;;; Lispy-Fennel (LF) --- The Functional Fennel Library
;;;
;;; Copyright (C) 2020 Kevin "The Nuclear" Bloom <nuclearkev@dragora.org>
;;;
;;; This file is part of LF.
;;;
;;; LF is free software: you can redistribute it and/or modify
;;; it under the terms of the MIT License.
;;;
;;; LF is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; MIT License for more details.
;;;
;;; You should have received a copy of the MIT License
;;; along with LF.  If not, see <https://opensource.org/licenses/MIT>.

(local view (include :fennelview))
(local list {})

(fn empty? [l]
  (not (next l)))

(fn list.empty? [l]
  (= 0 (length l)))

(fn list.range [str end]
  (var x str)
  (var l [])
  (while (not (= x end))
    (table.insert l x)
    (set x (+ 1 x)))
  list)

(fn list.nth [ind l]
  (. l ind))

(fn list.car [l]
  (. l 1))

(fn list.cdr [l]
  (var ls l)
  (table.remove ls 1)
  ls)

(fn list.cadr [l]
  (list.car
   (list.cdr
    l)))

(fn list.cddr [l]
  (list.cdr
   (list.cdr
    l)))

(fn list.append [l1 l2]
  (var total-list l1)
  (each
      [k v (ipairs l2)]
    (table.insert total-list v))
  total-list)

(fn list.last [l]
  (let [last-idx (length l)]
    (. l last-idx)))

(fn list.reverse [l]
  (var x [])
  (each
      [k v (pairs l)]
    (set x (list.append [v] x)))
  x)

(fn list.flatten [l]
  (var x [])
  (each [k v (ipairs l)]
    (if (and (= (type v) "table")
             (empty? v))
        nil
        (and (= (type v) "table")
             (. v 1))                   ;make sure it's a list not just a table
        (set x (list.append v x))
        (table.insert x v)))
  x)

(fn list.split [l n]
  (var idx 1)
  (var top [])
  (var rest l)
  (while (not (= idx n))
    (let [car (. rest 1)]
      (table.remove rest 1)
      (table.insert top car)
      (set idx (+ 1 idx))))
  [top rest])

(fn list.insert [l idx val]
  (let [split-list (list.split l idx)]
    (var top (. split-list 1))
    (var rest (. split-list 2))
    (table.insert top val)
    (list.append top rest)))

(fn list.split-when [f l]
  ;; (var idx 1)
  (var top [])
  (var rest l)
  (var done false)
  (while (not done)
    (let [car (. rest 1)]
      (if (list.empty? rest)
        (set done true)
        (f car)
        (set done true)
        (do
         (table.remove rest 1)
         (table.insert top car)))))
  [top rest])

(fn list.find [f l]
    (var val nil)
    (each [k v (ipairs l)]
          (let [found? (f v)]
            (when found?
              (set val v))))
    val)

(fn list.unique [l]
    (var uni [])
    (each [k v (ipairs l)]
          (when (not (list.find (fn [x] (= x v)) uni))
            (table.insert uni v))) uni)

list
